﻿using System;
using System.Collections.Generic;
using System.Linq;
using DivaScript;
using EditMode.Diva2nd;

namespace ScriptUtilities
{
    public class Diva2ndEditScript
    {
        const int DEFAULT_TARGET_DISTANCE = 1200 * 250;
        const int DEFAULT_TARGET_AMPLITUDE = 500;
        const int DEFAULT_TARGET_FREQUENCY = 2;

        const int DIVA_TIME_FACTOR = 100;

        public static (List<PvCommand> commands, PvDatabaseInfo pvDatabase) GetFtEditCommands(Edit edit, int pvId = 0)
        {
            PvDatabaseInfo database = new PvDatabaseInfo(edit, pvId);

            int startTimespan = (int)(edit.StartTimeSpan.TotalMilliseconds * -DIVA_TIME_FACTOR);
            int endTimespan = (int)(edit.GetTimeAt(edit.SongEndBeat).TotalMilliseconds * DIVA_TIME_FACTOR);

            List<PvCommand> pvCommands = new List<PvCommand>()
            {
                new PvCommand(startTimespan, "MUSIC_PLAY"),
                new PvCommand(endTimespan, "PV_END"),
                new PvCommand(endTimespan, "END"),
            };

            int currentSelectedPlayer = 0;

            TimeSpan time = TimeSpan.Zero;
            int lastBeat = 0, timestamp = 0;

            foreach (var editCommand in edit.PvCommands)
            {
                var opcode = editCommand.Opcode;
                string stringOpcode = opcode.ToString();

                List<int> param = new List<int>(editCommand.Parameters);

                switch (opcode)
                {
                    case Opcode.TIME:
                        {
                            lastBeat = param[0];
                            time = edit.GetTimeAt(lastBeat);

                            param[0] = (int)(time.TotalMilliseconds * DIVA_TIME_FACTOR + currentSelectedPlayer);
                            break;
                        }

                    case Opcode.EDIT_MOVE:
                    case Opcode.MOVE_CAMERA:
                        {
                            int duration = (int)edit.GetTimeAt(param[0]).TotalMilliseconds;

                            param = new List<int>() { duration };
                            for (int i = 2; i < editCommand.Parameters.Count; i++)
                                param.Add(editCommand.Parameters[i]);

                            break;
                        }

                    case Opcode.EDIT_LYRIC:
                        {
                            stringOpcode = "LYRIC";

                            int lyricsIndex = editCommand.Parameters[0];
                            int duration = editCommand.Parameters[1];

                            // start lyrics
                            param = new List<int>
                            {
                                lyricsIndex,
                                -1,
                            };

                            // end lyircs
                            TimeSpan endTime = edit.GetTimeAt(lastBeat + duration);
                            int endTimestamp = (int)(endTime.TotalMilliseconds * DIVA_TIME_FACTOR);
                            pvCommands.Add(new PvCommand(endTimestamp, stringOpcode, -1, -1));

                            break;
                        }

                    case Opcode.EDIT_TARGET:
                        {
                            stringOpcode = "TARGET";

                            int targetType = editCommand.Parameters[0];
                            int x = editCommand.Parameters[1];
                            int Y = editCommand.Parameters[2];
                            int angle = editCommand.Parameters[3];
                            int duration = editCommand.Parameters[4];

                            switch ((TargetType)targetType)
                            {
                                case TargetType.sankaku:
                                case TargetType.sankaku_w:
                                    targetType = (int)FtTargetType.sankaku;
                                    break;
                                case TargetType.maru:
                                case TargetType.maru_w:
                                    targetType = (int)FtTargetType.maru;
                                    break;
                                case TargetType.batsu:
                                case TargetType.batsu_w:
                                    targetType = (int)FtTargetType.batsu;
                                    break;
                                case TargetType.shikaku:
                                case TargetType.shikaku_w:
                                    targetType = (int)FtTargetType.shikaku;
                                    break;
                                case TargetType.sankaku_long:
                                    targetType = (int)FtTargetType.sankaku_hold;
                                    break;
                                case TargetType.maru_long:
                                    targetType = (int)FtTargetType.maru_hold;
                                    break;
                                case TargetType.batsu_long:
                                    targetType = (int)FtTargetType.batsu_hold;
                                    break;
                                case TargetType.shikaku_long:
                                    targetType = (int)FtTargetType.shikaku_hold;
                                    break;
                                default:
                                    break;
                            }

                            TimeSpan buttonTime = edit.GetTimeAt(lastBeat + Edit.BEATS_PER_BAR);
                            time = edit.GetTimeAt(lastBeat);

                            TimeSpan flyDuration = buttonTime - time;

                            timestamp = (int)(time.TotalMilliseconds * DIVA_TIME_FACTOR + currentSelectedPlayer);
                            pvCommands.Add(new PvCommand(timestamp, "TARGET_FLYING_TIME", (int)flyDuration.TotalMilliseconds));

                            param = new List<int>
                            {
                                targetType,
                                x,
                                Y,
                                angle,
                                DEFAULT_TARGET_DISTANCE,
                                DEFAULT_TARGET_AMPLITUDE,
                                DEFAULT_TARGET_FREQUENCY,
                            };

                            break;
                        }

                    case Opcode.SET_CHARA:
                        {
                            currentSelectedPlayer = param[0];
                            break;
                        }

                    case Opcode.EDIT_MOUTH:
                        {
                            //stringOpcode = "MOUTH_ANIM";
                            break;
                        }

                    case Opcode.EDIT_ITEM:
                        {
                            if (param[0] != -1)
                                param[0]++;
                            break;
                        }

                    case Opcode.EDIT_FACE:
                        break;
                    case Opcode.EDIT_SHADOW:
                        break;
                    case Opcode.EDIT_EYELID:
                        break;
                    case Opcode.EDIT_EYE:
                        break;
                    case Opcode.EDIT_EFFECT:
                        break;
                    case Opcode.EDIT_DISP:
                        break;
                    case Opcode.EDIT_BLUSH:
                        break;
                    default:
                        break;
                }

                timestamp = (int)(time.TotalMilliseconds * DIVA_TIME_FACTOR + currentSelectedPlayer);

                pvCommands.Add(new PvCommand(timestamp, stringOpcode, param));
            }

            pvCommands = PvScript.OrderListByTime(PvScript.AddTimeCommands(pvCommands.Where(t => t.Opcode != "TIME").ToList()));

            return (pvCommands, database);
        }
    }
}